在分頁和排序章節中,我們已經描述了如何讓終端使用者選擇要顯示的特定資料頁面,並依某些欄位排序。由於分頁和排序資料的任務非常常見,Yii 提供了一組資料提供器類別來封裝它。
資料提供器是一個實作 yii\data\DataProviderInterface 的類別。它主要支援檢索分頁和排序的資料。它通常用於與資料小工具一起使用,以便終端使用者可以互動式地分頁和排序資料。
Yii 發行版本中包含以下資料提供器類別
所有這些資料提供器的用法都遵循以下常見模式
// create the data provider by configuring its pagination and sort properties
$provider = new XyzDataProvider([
'pagination' => [...],
'sort' => [...],
]);
// retrieves paginated and sorted data
$models = $provider->getModels();
// get the number of data items in the current page
$count = $provider->getCount();
// get the total number of data items across all pages
$totalCount = $provider->getTotalCount();
您可以透過配置資料提供器的 pagination 和 sort 屬性來指定資料提供器的分頁和排序行為,這些屬性分別對應於 yii\data\Pagination 和 yii\data\Sort 的配置。您也可以將它們配置為 false
以停用分頁和/或排序功能。
資料小工具,例如 yii\grid\GridView,有一個名為 dataProvider
的屬性,它可以接受資料提供器實例並顯示它提供的資料。例如,
echo yii\grid\GridView::widget([
'dataProvider' => $dataProvider,
]);
這些資料提供器主要在指定資料來源的方式上有所不同。在以下小節中,我們將解釋每個資料提供器的詳細用法。
若要使用 yii\data\ActiveDataProvider,您應該配置其 query 屬性。它可以接受 yii\db\Query 或 yii\db\ActiveQuery 物件。如果前者,則傳回的資料將為陣列;如果後者,則傳回的資料可以是陣列或 Active Record 實例。例如,
use yii\data\ActiveDataProvider;
$query = Post::find()->where(['status' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'defaultOrder' => [
'created_at' => SORT_DESC,
'title' => SORT_ASC,
]
],
]);
// returns an array of Post objects
$posts = $provider->getModels();
如果以上範例中的 $query
是使用以下程式碼建立的,則資料提供器將傳回原始陣列。
use yii\db\Query;
$query = (new Query())->from('post')->where(['status' => 1]);
注意:如果查詢已指定
orderBy
子句,則終端使用者(透過sort
配置)給出的新排序指令將附加到現有的orderBy
子句。任何現有的limit
和offset
子句都將被終端使用者(透過pagination
配置)的分頁請求覆蓋。
預設情況下,yii\data\ActiveDataProvider 使用 db
應用程式組件作為資料庫連線。您可以透過配置 yii\data\ActiveDataProvider::$db 屬性來使用不同的資料庫連線。
yii\data\SqlDataProvider 使用原始 SQL 語句,該語句用於提取所需的資料。根據 sort 和 pagination 的規範,提供器將相應地調整 SQL 語句的 ORDER BY
和 LIMIT
子句,以僅提取所需順序的請求資料頁面。
若要使用 yii\data\SqlDataProvider,您應該指定 sql 屬性以及 totalCount 屬性。例如,
use yii\data\SqlDataProvider;
$count = Yii::$app->db->createCommand('
SELECT COUNT(*) FROM post WHERE status=:status
', [':status' => 1])->queryScalar();
$provider = new SqlDataProvider([
'sql' => 'SELECT * FROM post WHERE status=:status',
'params' => [':status' => 1],
'totalCount' => $count,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'attributes' => [
'title',
'view_count',
'created_at',
],
],
]);
// returns an array of data rows
$models = $provider->getModels();
資訊:只有在您需要分頁資料時才需要 totalCount 屬性。這是因為透過 sql 指定的 SQL 語句將被提供器修改,以僅傳回當前請求的資料頁面。提供器仍然需要知道資料項目的總數,以便正確計算可用的頁面數。
當使用大型陣列時,最好使用 yii\data\ArrayDataProvider。提供器允許您傳回按一個或多個欄位排序的陣列資料頁面。若要使用 yii\data\ArrayDataProvider,您應該將 allModels 屬性指定為大型陣列。大型陣列中的元素可以是關聯陣列(例如 DAO 的查詢結果)或物件(例如 Active Record 實例)。例如,
use yii\data\ArrayDataProvider;
$data = [
['id' => 1, 'name' => 'name 1', ...],
['id' => 2, 'name' => 'name 2', ...],
...
['id' => 100, 'name' => 'name 100', ...],
];
$provider = new ArrayDataProvider([
'allModels' => $data,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'attributes' => ['id', 'name'],
],
]);
// get the rows in the currently requested page
$rows = $provider->getModels();
注意:與 Active Data Provider 和 SQL Data Provider 相比,陣列資料提供器的效率較低,因為它需要將所有資料載入記憶體中。
當使用資料提供器傳回的資料項目時,您通常需要使用唯一鍵來識別每個資料項目。例如,如果資料項目代表客戶資訊,您可能希望使用客戶 ID 作為每個客戶資料的鍵。資料提供器可以傳回與 yii\data\DataProviderInterface::getModels() 傳回的資料項目相對應的此類鍵的清單。例如,
use yii\data\ActiveDataProvider;
$query = Post::find()->where(['status' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
]);
// returns an array of Post objects
$posts = $provider->getModels();
// returns the primary key values corresponding to $posts
$ids = $provider->getKeys();
在以上範例中,由於您向 yii\data\ActiveDataProvider 提供了 yii\db\ActiveQuery 物件,因此它足夠智慧,可以傳回主鍵值作為鍵。您也可以透過使用欄位名稱或計算鍵值的可呼叫物件配置 yii\data\ActiveDataProvider::$key 來明確指定應如何計算鍵值。例如,
// use "slug" column as key values
$provider = new ActiveDataProvider([
'query' => Post::find(),
'key' => 'slug',
]);
// use the result of md5(id) as key values
$provider = new ActiveDataProvider([
'query' => Post::find(),
'key' => function ($model) {
return md5($model->id);
}
]);
若要建立您自己的自訂資料提供器類別,您應該實作 yii\data\DataProviderInterface。更簡單的方法是從 yii\data\BaseDataProvider 擴充,這讓您可以專注於核心資料提供器邏輯。特別是,您主要需要實作以下方法
以下是有效讀取 CSV 資料的資料提供器範例
<?php
use yii\data\BaseDataProvider;
class CsvDataProvider extends BaseDataProvider
{
/**
* @var string name of the CSV file to read
*/
public $filename;
/**
* @var string|callable name of the key column or a callable returning it
*/
public $key;
/**
* @var SplFileObject
*/
protected $fileObject; // SplFileObject is very convenient for seeking to particular line in a file
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
// open file
$this->fileObject = new SplFileObject($this->filename);
}
/**
* {@inheritdoc}
*/
protected function prepareModels()
{
$models = [];
$pagination = $this->getPagination();
if ($pagination === false) {
// in case there's no pagination, read all lines
while (!$this->fileObject->eof()) {
$models[] = $this->fileObject->fgetcsv();
$this->fileObject->next();
}
} else {
// in case there's pagination, read only a single page
$pagination->totalCount = $this->getTotalCount();
$this->fileObject->seek($pagination->getOffset());
$limit = $pagination->getLimit();
for ($count = 0; $count < $limit; ++$count) {
$models[] = $this->fileObject->fgetcsv();
$this->fileObject->next();
}
}
return $models;
}
/**
* {@inheritdoc}
*/
protected function prepareKeys($models)
{
if ($this->key !== null) {
$keys = [];
foreach ($models as $model) {
if (is_string($this->key)) {
$keys[] = $model[$this->key];
} else {
$keys[] = call_user_func($this->key, $model);
}
}
return $keys;
}
return array_keys($models);
}
/**
* {@inheritdoc}
*/
protected function prepareTotalCount()
{
$count = 0;
while (!$this->fileObject->eof()) {
$this->fileObject->next();
++$count;
}
return $count;
}
}
雖然您可以如過濾資料和分離的篩選表單章節中所述,手動為 active data provider 建構條件,但如果您需要彈性的篩選條件,Yii 具有非常有用的資料篩選器。資料篩選器可以如下使用
$filter = new ActiveDataFilter([
'searchModel' => 'app\models\PostSearch'
]);
$filterCondition = null;
// You may load filters from any source. For example,
// if you prefer JSON in request body,
// use Yii::$app->request->getBodyParams() below:
if ($filter->load(\Yii::$app->request->get())) {
$filterCondition = $filter->build();
if ($filterCondition === false) {
// Serializer would get errors out of it
return $filter;
}
}
$query = Post::find();
if ($filterCondition !== null) {
$query->andWhere($filterCondition);
}
return new ActiveDataProvider([
'query' => $query,
]);
PostSearch
模型用於定義允許哪些屬性和值進行篩選
use yii\base\Model;
class PostSearch extends Model
{
public $id;
public $title;
public function rules()
{
return [
['id', 'integer'],
['title', 'string', 'min' => 2, 'max' => 200],
];
}
}
資料篩選器非常靈活。您可以自訂條件的建構方式以及允許哪些運算符。如需詳細資訊,請查看關於 yii\data\DataFilter 的 API 文件。
發現錯字或您認為此頁面需要改進嗎?
在 github 上編輯 !
註冊或登入才能留言。